
import {ArrayMethods} from "./arr"

// 处理data数据是对象的情况

export function observer(data) {
    // 判断 ,data一定要是一个对象，判断传递过来的数据需不需要进行劫持
    if (typeof data != 'object' || data == null) {
        return data;
    }
    // 1 对象 通过一个类对数据进行劫持
    return new Observer(data)
}

class Observer {
    constructor(value){
        // console.log(value);

        // 给value定义一个属性，这个value就是我们的值,在对数据进行添加的时候能够用到
        Object.defineProperty(value,"__ob__",{  // 给一个对象上添加新属性
            enumerable:false,   //这个值不能进行枚举,不能被遍历
            value:this  // 这个this指向我们的这个实例，该属性添加的对应值
        })


        if(Array.isArray(value)){    //处理数据是数组的时候
            // 给value（这里代表的是一个数组）的原型上添加一个方法

            // 如果处理的值是普通数组
            value.__proto__= ArrayMethods
            // 如果处理的值是数组对象   list:[{a:1}]
            this.observeArray(value)  // 将数组的值进行劫持
        }else{
            this.walk(value) //遍历里面的数据,处理数据是对象的时候
        }        
    }
    walk(data){  
        let keys = Object.keys(data) //拿到对象中的key值，这个方法将所有的key值变成一个数组
        for(let i = 0;i<keys.length;i++){
            // 对对像中的每一个元素进行劫持
            let key = keys[i]
            let value = data[key]
            // 对对象中的某个值进行劫持,传递三个参数
            defineReactive(data,key,value)
        }
    }
    observeArray(value){ // [{a;1}]
        for (let i=0;i<value.length;i++){
            observer(value[i])  // 对数据中的每一项都进行数据类型判断
        }
    }
}   

// 对对象中的数据进行劫持
function defineReactive(data,key,value){
    observer(value)  // 深度代理，将所有的数据进行劫持，采取递归的方式
    Object.defineProperty(data,key,{
        // 获取到某个值的时候打印
        get(){  
            console.log('获取');
            return value
        },
        // 修改某个数据时进行劫持
        set(newValue){
            console.log("设置");
            if(newValue === value) return //如果改变的值和原来的值一样，那么就不需要进行劫持
            observer(newValue)   // 用户设置的值是对象
            value = newValue  //对数据进行劫持
        }
    })
}

// 在vue2中对数据进行劫持是通过 Object.defineProperty 进行劫持的（他的get和set方法）
// 缺点：他只能对对象中的某一个数据进行劫持  {a:1,b:2}，所以在vue3中将他放弃了,在vue2中通过循环对象中的数据进行劫持所有数据

// {a:{} ,list:[]}

// 总结：   1 对象
    //  Object.defineProperty 有缺点只能对对象中的一个属性进行劫持
    //  遍历  只能对对象只中的第一层进行劫持  a:{b:10 ,obj:{}}
    //  递归  get  和  set


    // 数组劫持   {list:[1,2,3],arr:[{a:1}]}  大概是这两种情况
    // 对方法函数进行劫持，劫持数组方法


    // 如果data中的数据是对象那么通过Object.defineProperty 来进行劫持
    // 如果data中的数据是数组，那么需要通过劫持数组方法完成的，需要重写数组中的方法来进行完成
